package article

import (
	"context"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
	"webook/internal/repository/dao"
)

type GORMArticleDAO struct {
	db *gorm.DB
}

func NewGORMArticleDAO(db *gorm.DB) dao.ArticleDAO {
	return &GORMArticleDAO{
		db: db,
	}
}

// ListPub 分页获取线上库文章列表
func (articleDAO *GORMArticleDAO) ListPub(ctx context.Context, offset int, limit int) ([]dao.Article, error) {
	var arts []dao.Article
	err := articleDAO.db.WithContext(ctx).
		Offset(offset).
		Limit(limit).
		Order("utime desc").
		Find(&arts).Error
	if err != nil {
		return []dao.Article{}, err
	}
	return arts, nil
}

// GetPubById 根据id查询线上库文章详情
func (articleDAO *GORMArticleDAO) GetPubById(ctx context.Context, articleId int64) (dao.PublishArticle, error) {
	var pubArt dao.PublishArticle
	err := articleDAO.db.WithContext(ctx).
		Where("id=?", articleId).
		Find(&pubArt).Error
	if err != nil {
		return dao.PublishArticle{}, err
	}
	return pubArt, nil
}

// GetById 根据id查询制作库文章详情
func (articleDAO *GORMArticleDAO) GetById(ctx context.Context, authorId int64, articleId int64) (dao.Article, error) {
	var art dao.Article
	err := articleDAO.db.WithContext(ctx).
		Where("id=? and author_id=?", articleId, authorId).
		Find(&art).Error
	if err != nil {
		// 可能有人在搞你
		// TODO 记录日志
		return dao.Article{}, err
	}
	return art, err
}

// FindByAuthorId 根据作者id查询文章列表（分页）
func (articleDAO *GORMArticleDAO) FindByAuthorId(ctx context.Context, authorId int64, offset int, limit int) ([]dao.Article, error) {
	var articles []dao.Article
	err := articleDAO.db.WithContext(ctx).
		Where("author_id=?", authorId).
		Offset(offset).
		Limit(limit).
		Order("utime desc").
		Find(&articles).Error
	if err != nil {
		return nil, err
	}
	return articles, nil
}

// Sync dao层完成制作库和线上库数据同步
func (articleDAO *GORMArticleDAO) Sync(ctx context.Context, article dao.Article) (int64, error) {
	// 开启事务
	tx := articleDAO.db.WithContext(ctx).Begin()
	if tx.Error != nil {
		return 0, tx.Error
	}
	// 注册事务回滚
	defer tx.Rollback()
	// 操作制作库
	authorDAO := NewGORMArticleDAO(tx)
	var (
		id  int64
		err error
	)
	if article.Id > 0 {
		id, err = authorDAO.Update(ctx, article)
	} else {
		id, err = authorDAO.Insert(ctx, article)
	}
	if err != nil {
		return 0, err
	}
	// 复制id
	var pubArt dao.PublishArticle
	pubArt.Id = id
	pubArt.Title = article.Title
	pubArt.Content = article.Content
	pubArt.Ctime = article.Ctime
	pubArt.Utime = article.Utime
	pubArt.AuthorId = article.AuthorId
	// 操作线上库
	readerDAO := NewGORMArticleDAO(tx)
	id, err = readerDAO.Upsert(ctx, pubArt)
	if err == nil {
		// 提交事务
		tx.Commit()
		return id, nil
	}
	return 0, err
}

func (dao *GORMArticleDAO) Insert(ctx context.Context, article dao.Article) (int64, error) {
	now := time.Now().UnixMilli()
	article.Ctime = now
	article.Utime = now
	err := dao.db.WithContext(ctx).Create(&article).Error
	if err != nil {
		return 0, err
	}
	return article.Id, nil
}

// Upsert SQL upsert语法
func (dao *GORMArticleDAO) Upsert(ctx context.Context, article dao.PublishArticle) (int64, error) {
	// 等价于insert xxx on duplicate key update xxx
	now := time.Now().UnixMilli()
	article.Utime = now
	article.Ctime = now
	err := dao.db.WithContext(ctx).Clauses(clause.OnConflict{
		DoUpdates: clause.Assignments(map[string]any{
			"title":   article.Title,
			"content": article.Content,
			"utime":   article.Utime,
		}),
	}).Create(&article).Error
	if err != nil {
		return 0, err
	}
	return article.Id, nil
}

func (dao *GORMArticleDAO) Update(ctx context.Context, article dao.Article) (int64, error) {
	now := time.Now().UnixMilli()
	article.Utime = now
	// 如何防止黑客恶意伪造其他id修改其他文章的数据
	// 思路一：先查后改
	// 思路二：按照条件修改
	err := dao.db.WithContext(ctx).Model(&article).
		Where("id=? and author_id=?", article.Id, article.AuthorId).
		Updates(map[string]any{
			"title":   article.Title,
			"content": article.Content,
			"utime":   article.Utime,
		}).Error
	if err != nil {
		return 0, err
	}
	return article.Id, nil
}
